#include <cstdio>
#include "glew/glew.h"
#include "Font.hpp"
#include "Color.hpp"

namespace fge
{

static uint16_t Deserialize(std::FILE *file)
{
	uint8_t bytes[2];
	std::fread(&bytes, 1, 2, file);
	return (static_cast<uint16_t>(bytes[0]) << 8) | bytes[1];
}

Font::~Font()
{
	if(textureId != 0)
		glDeleteTextures(1, &textureId);
}

Font* Font::FromFile(const std::string &filename)
{
	Font *font = new Font();

	std::FILE *file = std::fopen(filename.c_str(), "rb");
	if(!file)
		return NULL;

	// version (uint16_t)
	// font size (uint16_t)
	// texture width (uint16_t)
	// texture height (uint16_t)
	// number of characters (uint16_t)
	// foreach character
	//		uint16_t character
	//		uint16_t width, height;
	//		uint16_t offsetX, offsetY;
	//		uint16_t advanceX, advanceY;
	//		uint16_t textureX, textureY;
	// texture (array of uint8_t)

	// nothing to do with version currently, but when future versions are created, add code here
	uint16_t version = Deserialize(file);
	font->height = Deserialize(file);
	uint16_t textureWidth = Deserialize(file);
	uint16_t textureHeight = Deserialize(file);
	uint16_t characterCount = Deserialize(file);

	for(int i=0; i<characterCount; i++)
	{
		CharacterData characterData;
		uint16_t character = Deserialize(file);
		characterData.width = Deserialize(file);
		characterData.height = Deserialize(file);
		characterData.offsetX = Deserialize(file);
		characterData.offsetY = Deserialize(file);
		characterData.advanceX = Deserialize(file);
		characterData.advanceY = Deserialize(file);
		uint16_t textureX = Deserialize(file);
		uint16_t textureY = Deserialize(file);
		characterData.u1 = static_cast<float>(textureX) / textureWidth;
		characterData.v1 = static_cast<float>(textureY) / textureHeight;
		characterData.u2 = static_cast<float>(textureX + characterData.width) / textureWidth;
		characterData.v2 = static_cast<float>(textureY + characterData.height) / textureHeight;

		font->characters[character] = characterData;
	}

	uint8_t *pixels = new uint8_t[textureWidth * textureHeight];
	std::fread(pixels, 1, textureWidth * textureHeight, file);
	std::fclose(file);

	glGenTextures(1, &font->textureId);
	glBindTexture(GL_TEXTURE_2D, font->textureId);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, textureWidth, textureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);

	delete[] pixels;

	return font;
}

Vector2 Font::MeasureString(const std::string &s)
{
	int sum = 0;

	for(size_t i=0; i<s.size(); i++)
	{
		sum += GetCharacterData(static_cast<uint16_t>(s[i])).advanceX;
	}

	return Vector2(static_cast<float>(sum), static_cast<float>(height));
}

void Font::DrawString(const std::string& s, const Vector2 &position, const Color &color)
{
	float x = position.X();
	float y = position.Y();

	glBindTexture(GL_TEXTURE_2D, textureId);
	glBegin(GL_QUADS);

	for(size_t i=0; i<s.size(); i++)
	{
		if(s[i] == '\n' || s[i] == '\r')
		{
			x = position.X();
			y += height;
			continue;
		}

		CharacterData &cd = GetCharacterData(static_cast<uint16_t>(s[i]));

		if(cd.width > 0 && cd.height > 0)
		{
			glColor4f(color.R(), color.G(), color.B(), color.A());
			glTexCoord2f(cd.u1, cd.v1);
			glVertex2f(x + cd.offsetX, y + -cd.offsetY);
			glColor4f(color.R(), color.G(), color.B(), color.A());
			glTexCoord2f(cd.u2, cd.v1);
			glVertex2f(x + cd.offsetX + cd.width, y + -cd.offsetY);
			glColor4f(color.R(), color.G(), color.B(), color.A());
			glTexCoord2f(cd.u2, cd.v2);
			glVertex2f(x + cd.offsetX + cd.width, y + -cd.offsetY + cd.height);
			glColor4f(color.R(), color.G(), color.B(), color.A());
			glTexCoord2f(cd.u1, cd.v2);
			glVertex2f(x + cd.offsetX, y + -cd.offsetY + cd.height);
		}
		x += cd.advanceX;
	}

	glEnd();
}

}
